<?php
//+---------------------------------------------------------------------------------------------------------------------
//| 人生是荒芜的旅行，冷暖自知，苦乐在心
//+---------------------------------------------------------------------------------------------------------------------
//| Author:Janmas <janmas@126.com>
//+---------------------------------------------------------------------------------------------------------------------
//| 
//+---------------------------------------------------------------------------------------------------------------------


namespace helper\upload;


use think\exception\ValidateException;
use think\File;

abstract class BaseUpload
{
	protected $config;

    /**
     * @var self
     */
	public static $instance;

	/**
	 * 文件信息
	 * @var array
	 */
	protected $fileInfo = [];

    protected $docExt = ['txt', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'csv', 'pdf', 'md'];
    protected $imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp',];
    protected $videoExt = ['mp4', 'ogg', 'm4a',];

	public function __construct($config){
		$this->config = $config;
	}


	public static function instance(Array $config=[] ){
		if(!self::$instance instanceof static){
			self::$instance = new static($config);
		}

		return self::$instance;
	}

	/**
	 * 上传
	 * @param File|null $file
	 * @return self
	 */
	abstract public function upload(File $file=null);

    /**
     * 废弃
     * @param File|null $file
     */
	protected function save(File &$file=null){}

    /**
     * 文件检测
     * @param File $file
     * @return bool
     * @throws \Exception
     */
	protected function validate(File &$file){
		if(is_null($file)) throw new Exception('请选择上传的文件');
        //检查有没有有木马
        $this->trojan($file);

		$mime = substr($file->getMime(),0,strpos($file->getMime(),'/'));
		$mime = $mime == 'image'?$mime:'file';
		try{
			validate([ $mime => 'fileSize:' . ($this->config[$mime . '_size'] / 1024) . '|fileExt:' . $this->config[$mime . '_ext'] ])->failException(true)->check([$file]);

            $this->fileInfo = [
                'size' => $file->getSize(),
                'md5' => $file->md5(),
                'ext'=> $file->getOriginalExtension(),
                'name' => $file->getOriginalName(),
                'memi' => $file->getMime(),
                'driver' => lcfirst($this->getClassName()),
                'thumb' => '',
                'path' => ''
            ];

			return true;
		}catch (ValidateException $e){
			throw new Exception($e->getMessage());
		}
	}

	public function getFileInfo(){
		return $this->fileInfo;
	}

	protected function getClassName(){
		$class =explode('\\', static::class);
		return (array_pop($class));
	}

	protected function isVideo(File &$file){
        if(in_array($file->extension(),$this->videoExt)){
            return true;
        }
        return false;
    }

    /**
     * 木马检测
     * @param File|null $file
     * @return int
     */
    protected function trojan(File &$file=null){
        if(file_exists($file->getPathname())){
            $resource = fopen($file->getPathname(),'rb');
            $fileSize = $file->getSize();
            fseek($resource,0);
            if($fileSize > 512){
                $hexCode = bin2hex(fread($resource, 512));
                fseek($resource, $fileSize - 512); //把文件指针移到文件尾部
                $hexCode .= bin2hex(fread($resource, 512));
            }else{
                $hexCode = bin2hex(fread($resource, $fileSize));
            }
            fclose($resource);
            /* 匹配16进制中的 <% ( ) %> */
            /* 匹配16进制中的 <? ( ) ?> */
            /* 匹配16进制中的 <script | /script> 大小写亦可*/
            /* 核心  整个类检测木马脚本的核心在这里  通过匹配十六进制代码检测是否存在木马脚本*/

            if (preg_match("/(3c25.*?28.*?29.*?253e)|(3c3f.*?28.*?29.*?3f3e)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexCode)){
                throw new Exception('未能通过安全检测');
            }
        }else{
            throw new \Exception('临时文件丢失');
        }
	    return true;
    }

}